/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "HistoryWnd.h"

// project's headers 
#include "Alert.h"
#include "Assert.h"
#include "Language.h"
#include "Resources.h"
#include "Version.h"

// standard headers
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>


/******************************************************************/
/* Internal constantes                                            */
/******************************************************************/

// List-View Sub Items
#define LVSI_TIME		0
#define LVSI_DESCRIPTION	1

// Windows Messages
#define WM_FILLLIST		(WM_USER+5)

// Buffer size in characters
#define BUFFER_SIZE		1024

// Window class
#define WC_HISTORY		TEXT("History")

#define CX_VIEW_BUTTON		55
#define CY_VIEW_BUTTON		55

LPCTSTR szRegeditKeyPath = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit") ;
LPCTSTR szLastKey = TEXT("LastKey") ;


/******************************************************************/
/* Internal variables                                             */
/******************************************************************/

static HWND hwndHistory ;
static int cxChar, cyChar ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

LRESULT CALLBACK _HistoryWnd_WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ;

BOOL _HistoryWnd_BrowseDirectory  (HWND hwnd, LPCTSTR szDir) ;

BOOL _HistoryWnd_BrowseRegKey (HWND hwnd, LPCTSTR szKey) ;

BOOL _HistoryWnd_DownloadPage (HWND hwnd) ;

BOOL _HistoryWnd_BugReportPage (HWND hwnd) ;


/******************************************************************/
/* Exported function : HistoryWnd_CreateDialog                    */
/******************************************************************/

BOOL HistoryWnd_RegisterClass (HINSTANCE hInstance)
{
  WNDCLASS wc = {
    .lpszClassName	= WC_HISTORY,
    .lpfnWndProc	= _HistoryWnd_WndProc,
    .hCursor		= LoadCursor (NULL, IDC_ARROW),
    .hbrBackground	= (HBRUSH)( COLOR_BTNFACE + 1 ),
    .hIcon		= LoadIcon (hInstance, MAKEINTRESOURCE(IDI_APP)) 
  } ;

  return 0!=RegisterClass(&wc) ;
}


/******************************************************************/
/* Exported function : HistoryWnd_CreateWindow                    */
/******************************************************************/

BOOL HistoryWnd_CreateWindow (HINSTANCE hInstance)
{
  int x, y ;
  int cxWindow, cyWindow ;
  int cxScreen, cyScreen ;
  HDC hdc ;
  TEXTMETRIC tm ;

  // dialog alredy exists ?
  if( hwndHistory && IsWindow(hwndHistory) ) 
    return SetForegroundWindow (hwndHistory) ;

  // get char size
  hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL) ;
  SelectObject (hdc, GetStockObject(DEFAULT_GUI_FONT)) ;
  GetTextMetrics (hdc, &tm) ;
  cxChar = tm.tmAveCharWidth ;
  cyChar = tm.tmHeight + tm.tmExternalLeading ;
  DeleteDC (hdc) ;

  // calc window pos
  cxWindow = 500 ;
  cyWindow = 300 ;
  cxScreen = GetSystemMetrics (SM_CXFULLSCREEN) ;
  cyScreen = GetSystemMetrics (SM_CYFULLSCREEN) ;
  x = (cxScreen-cxWindow) / 2 ;
  y = (cyScreen-cxWindow) / 2 ;
  
  // create the dialog box
  hwndHistory = CreateWindow (WC_HISTORY, STR(_LAST_ALERTS),
			      WS_OVERLAPPEDWINDOW|WS_VISIBLE, 
			      x, y, cxWindow, cxWindow,
			      NULL, NULL, hInstance, NULL) ;

  ASSERT (hwndHistory!=NULL) ;

  return hwndHistory!=NULL ;
}


/******************************************************************/
/* Exported function : HistoryWnd_DestroyWindow                   */
/******************************************************************/

VOID HistoryWnd_DestroyWindow ()
{
  // destroy window if needed
  if( hwndHistory )
    DestroyWindow (hwndHistory) ;

  // hwndHistoryWnd is set to NULL in WndProc
}


/******************************************************************/
/* Exported function : HistoryWnd_SendMessage                     */
/******************************************************************/

void HistoryWnd_SendMessage (UINT message, WPARAM wParam, LPARAM lParam) 
{
  if( hwndHistory )
    SendMessage (hwndHistory, message, wParam, lParam) ;
}


/******************************************************************/
/* Internal function : _HistoryWnd_WndProc                         */
/******************************************************************/

LRESULT CALLBACK _HistoryWnd_WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
  int		i, iWidth, iHeight ;
  LINKED_LIST	*pHist ;
  LINKED_NODE	*pNode ;
  LV_COLUMN	lvc ;
  LV_ITEM	lvi ;
  TCHAR		szBuffer[BUFFER_SIZE] ;
  union {
    NMHDR	*pnmh ;
    NM_LISTVIEW *pnmlv ;
    LPARAM	lParam ;
  } nmu ;

  enum {
    LIST,
    DESC_LABEL,
    DESC,
    ADVICE_LABEL,
    ADVICE,
    VIEW,
    CHILDREN_COUNT 
  } ;
  
  static HWND		aChildren[CHILDREN_COUNT] ;
  static HINSTANCE	hInstance ;
  static ALERT		*pAlert ;


  switch( message )
    {
    case WM_CREATE:

      hInstance = ((CREATESTRUCT*)lParam)->hInstance ;

      pAlert = NULL ;

      aChildren[DESC_LABEL] = CreateWindow (WC_BUTTON, STR(_DESCRIPTION),
					    WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 0, 0, 1, 1,
					    hwnd, (HMENU)DESC_LABEL, hInstance, NULL) ;

      aChildren[DESC] = CreateWindow (WC_EDIT, NULL,
				      WS_CHILD|WS_VISIBLE|ES_READONLY|ES_MULTILINE, 0, 0, 1, 1,
				      hwnd, (HMENU)DESC, hInstance, NULL) ;

      aChildren[ADVICE_LABEL] = CreateWindow (WC_BUTTON, STR(_ADVICE),
					    WS_CHILD|WS_VISIBLE|BS_GROUPBOX, 0, 0, 1, 1,
					    hwnd, (HMENU)ADVICE_LABEL, hInstance, NULL) ;

      aChildren[ADVICE] = CreateWindow (WC_EDIT, NULL,
					WS_CHILD|WS_VISIBLE|ES_READONLY|ES_MULTILINE, 0, 0, 1, 1,
					hwnd, (HMENU)ADVICE, hInstance, NULL) ;

      aChildren[VIEW] = CreateWindow (WC_BUTTON, NULL,
				      WS_CHILD|WS_VISIBLE|BS_ICON, 0, 0, 1, 1,
				      hwnd, (HMENU)VIEW, hInstance, NULL) ;      

 
      // set icon of "view" button
      SendMessage (aChildren[VIEW], BM_SETIMAGE, IMAGE_ICON, 
      		   (LPARAM)LoadImage(hInstance,MAKEINTRESOURCE(IDI_APP),IMAGE_ICON,48,48,0)) ;

      // get list hwnd
      aChildren[LIST] = CreateWindowEx (WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
					WS_CHILD|WS_VISIBLE| 
					LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS, 
					0, 0, 1, 1,
					hwnd, (HMENU)LIST, hInstance, NULL) ;

      // set ex style of list
      ListView_SetExtendedListViewStyle (aChildren[LIST], LVS_EX_FULLROWSELECT) ;

      // insert time column
      lvc.mask		= LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM ;
      lvc.cx		= 50 ;
      lvc.pszText	= (LPTSTR) STR(_TIME) ;
      lvc.iSubItem	= LVSI_TIME ;
      ListView_InsertColumn (aChildren[LIST], 0, &lvc) ;

      // insert description column
      lvc.mask		= LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM ;
      lvc.cx		= 435 ;
      lvc.pszText	= (LPTSTR) STR(_DESCRIPTION) ;
      lvc.iSubItem	= LVSI_DESCRIPTION ;
      ListView_InsertColumn (aChildren[LIST], 1, &lvc) ;

      // fill list
      SendMessage (hwnd, WM_FILLLIST, 0, 0) ;

      // set font of each child
      for( i=0 ; i<CHILDREN_COUNT ; i++ )
	SendMessage (aChildren[i], WM_SETFONT, 
		     (LPARAM)GetStockObject(DEFAULT_GUI_FONT),
		     TRUE) ;

      return 0 ;


    case WM_LANGUAGECHANGED:

      SetWindowText (hwnd, STR(_LAST_ALERTS)) ;
      SetWindowText (aChildren[DESC_LABEL], STR(_DESCRIPTION)) ;
      SetWindowText (aChildren[ADVICE_LABEL], STR(_ADVICE)) ;

      // change time column title
      lvc.mask		= LVCF_TEXT ;
      lvc.pszText	= (LPTSTR) STR(_TIME) ;
      ListView_SetColumn (aChildren[LIST], 0, &lvc) ;  
      
      // change description column title
      lvc.mask		= LVCF_TEXT ;
      lvc.pszText	= (LPTSTR) STR(_DESCRIPTION) ;
      ListView_SetColumn (aChildren[LIST], 1, &lvc) ;   

      // re-fill list
      SendMessage (hwnd, WM_FILLLIST, 0, 0) ;
      
      return 0 ; // case WM_LANGUAGECHANGED:

      
    case WM_SIZE:

      iWidth  = LOWORD (lParam) ;
      iHeight = HIWORD (lParam) ;

      MoveWindow (aChildren[LIST], 
		  0, 0, 
		  iWidth, iHeight/3, 
		  TRUE) ;

      MoveWindow (aChildren[DESC_LABEL],
		  0, iHeight/3,
		  iWidth, (2*iHeight/3)/4,
		  TRUE) ;

      MoveWindow (aChildren[DESC],
		  cxChar, iHeight/3+3*cyChar/2,
		  iWidth-cxChar*2, (2*iHeight/3)/4-10-2*cyChar,
		  TRUE) ;
      
      MoveWindow (aChildren[ADVICE_LABEL],
		  0, iHeight/3+(2*iHeight/3)/4,
		  iWidth, 3*(2*iHeight/3)/4,
		  TRUE) ;
      
      MoveWindow (aChildren[ADVICE],
		  cxChar, iHeight/3+(2*iHeight/3)/4+3*cyChar/2,
		  iWidth-cxChar*2, 3*(2*iHeight/3)/4-3*cyChar-CY_VIEW_BUTTON,
		  TRUE) ;
      
      MoveWindow (aChildren[VIEW],
		  iWidth-2*cxChar-CX_VIEW_BUTTON,
		  iHeight-cyChar-CY_VIEW_BUTTON,
		  CX_VIEW_BUTTON,
		  CY_VIEW_BUTTON,
		  TRUE) ;

      return 0 ;

    case WM_FILLLIST:
      
      // retreive history
      pHist = Alert_GetHistory () ;

      ListView_DeleteAllItems (aChildren[LIST]) ;

      i = 0 ;
      for( pNode=pHist->front ; pNode ; pNode=pNode->next )
	{
	  pAlert = pNode->data ;

	  _stprintf (szBuffer, TEXT("%.02d:%.02d:%.02d"),
		     pAlert->time.wHour, 
		     pAlert->time.wMinute,
		     pAlert->time.wSecond) ;

	  lvi.mask	= LVIF_TEXT | LVIF_PARAM;
	  lvi.iItem	= i++ ;
	  lvi.iSubItem	= LVSI_TIME ;
	  lvi.pszText	= szBuffer ;
	  lvi.lParam	= (LPARAM)pAlert ;
	  ListView_InsertItem (aChildren[LIST], &lvi) ;
	  
	  Alert_GetString (szBuffer, BUFFER_SIZE, pAlert) ;
	  
	  lvi.mask	= LVIF_TEXT ;
	  lvi.iSubItem	= LVSI_DESCRIPTION ;
	  lvi.pszText	= szBuffer ;
	  ListView_SetItem (aChildren[LIST], &lvi) ;
	}      

      return 0 ; // case WM_FILLLIST:


    case WM_COMMAND:

      if( LOWORD(wParam)==VIEW && HIWORD(wParam)==BN_CLICKED && pAlert )
	{
	  switch( pAlert->id & 0xFF0000 )
	    {
	    case ALERT_CLASS_REGISTRY:
	      _HistoryWnd_BrowseRegKey (hwnd, pAlert->args[0]) ;
	      break ;
	    case ALERT_CLASS_DIRECTORY:
	      _HistoryWnd_BrowseDirectory (hwnd, pAlert->args[0]) ;
	      break ;
	    case ALERT_CLASS_UPDATE:
	      _HistoryWnd_DownloadPage (hwnd) ;
	      break ;
	    default:
	      if( pAlert->advice==ADVICE_SEND_BUG_REPORT )
		_HistoryWnd_BugReportPage (hwnd) ;
	    }   

	  return 0 ;
	}

      break ; // case WM_COMMAND:

      
    case WM_NOTIFY:
      
      nmu.lParam = lParam ;
      
      switch( nmu.pnmh->code )
	{
	case LVN_ITEMCHANGED:
	  
	  if( nmu.pnmlv->uNewState & LVIS_SELECTED )
	    {
	      pAlert = (ALERT*) nmu.pnmlv->lParam ;
	      Alert_GetString (szBuffer, BUFFER_SIZE, pAlert) ;
	      SetWindowText (aChildren[DESC], szBuffer) ;
	      Alert_GetAdvice (szBuffer, BUFFER_SIZE, pAlert) ;
	      SetWindowText (aChildren[ADVICE], szBuffer) ;
	    } 
	  
	  break ;
	}

      break ;


    case WM_ALERT:
      
      pAlert = (ALERT*)lParam ;
      
      _stprintf (szBuffer, TEXT("%.02d:%.02d:%.02d"),
		 pAlert->time.wHour, 
		 pAlert->time.wMinute,
		 pAlert->time.wSecond) ;
      
      lvi.mask		= LVIF_TEXT | LVIF_PARAM;
      lvi.iItem		= 0 ;
      lvi.iSubItem	= LVSI_TIME ;
      lvi.pszText	= szBuffer ;
      lvi.lParam	= (LPARAM)pAlert ;
      ListView_InsertItem (aChildren[LIST], &lvi) ;
      
      Alert_GetString (szBuffer, BUFFER_SIZE, pAlert) ;
      
      lvi.mask	= LVIF_TEXT ;
      lvi.iSubItem	= LVSI_DESCRIPTION ;
      lvi.pszText	= szBuffer ;
      ListView_SetItem (aChildren[LIST], &lvi) ;  

      // delete history that are no more in the history
      while( ListView_GetItemCount(aChildren[LIST]) > ALERT_HISTORY_SIZE )
	ListView_DeleteItem (aChildren[LIST],ListView_GetItemCount(aChildren[LIST])-1) ;     

      return 0 ;  // case WM_ALERT:
      

 /*   case WM_CLOSE:

      DestroyWindow (hwnd) ;   
   
      return 0 ;*/


    case WM_DESTROY:
      
      hwndHistory = NULL ;
			   

      return 0 ;
    }
  
  return DefWindowProc (hwnd, message, wParam, lParam) ;
}


/******************************************************************/
/* Internal function : BrowseDirectory                            */
/******************************************************************/

BOOL _HistoryWnd_BrowseDirectory  (HWND hwnd, LPCTSTR szDir) 
{
  return (DWORD)ShellExecute (hwnd, TEXT("explore"), szDir, NULL, NULL, SW_SHOW) > 32 ;
}

BOOL _HistoryWnd_BrowseRegKey (HWND hwnd, LPCTSTR szKey) 
{
  HKEY hkey ;
  TCHAR szRoot[8] ;
  TCHAR szPath[MAX_PATH] ;
  int n ;
  
  n = _tcscspn (szKey, TEXT("\\")) ;
  _tcsncpy (szRoot, szKey, n) ;
  szRoot[n] = 0 ;

  if( !_tcscmp(szRoot,TEXT("HKCR")) )
    _tcscpy (szPath, TEXT("HKEY_CLASSES_ROOT")) ;
  else if( !_tcscmp(szRoot,TEXT("HKCU")) )
    _tcscpy (szPath, TEXT("HKEY_CURRENT_USER")) ;
  else if( !_tcscmp(szRoot,TEXT("HKLM")) )
    _tcscpy (szPath, TEXT("HKEY_LOCAL_MACHINE")) ;
  else if( !_tcscmp(szRoot,TEXT("HKU")) )
    _tcscpy (szPath, TEXT("HKEY_USERS")) ;
  else if( !_tcscmp(szRoot,TEXT("HKCC")) )
    _tcscpy (szPath, TEXT("HKEY_CURRENT_CONFIG")) ;
  else
    ASSERT (!"Invalid root key") ;
  
  _tcscat (szPath, szKey+n) ;
  
  RegCreateKeyEx (HKEY_CURRENT_USER, szRegeditKeyPath, 0,
		  TEXT(""), REG_OPTION_NON_VOLATILE, 
		  KEY_SET_VALUE, NULL, &hkey, NULL) ;
  RegSetValueEx (hkey, szLastKey, 0, REG_SZ, 
		 (BYTE*)szPath, 
		 (_tcslen(szPath)+1)*sizeof(TCHAR)) ; 
  RegFlushKey (hkey) ;
  RegCloseKey (hkey) ;

  return (DWORD)ShellExecute (hwnd, NULL, TEXT("regedit.exe"), NULL, NULL, SW_SHOW) > 32 ;	
}

BOOL _HistoryWnd_DownloadPage (HWND hwnd) 
{
  return (DWORD)ShellExecute (hwnd, NULL, TEXT(WEB_SITE_ADDRESS), NULL, NULL, SW_SHOW) > 32 ;
}

BOOL _HistoryWnd_BugReportPage (HWND hwnd) 
{
  return (DWORD)ShellExecute (hwnd, NULL, TEXT(BUG_REPORT_PAGE), NULL, NULL, SW_SHOW) > 32 ;
}
